package com.hwtx.form.util;

import cn.hutool.core.util.NumberUtil;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.stream.Streams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;

import javax.tools.*;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.stream.Collectors;

public class BeanUtil {

    private static final Logger log = LoggerFactory.getLogger(BeanUtil.class);

    public static boolean registrySpringBean(Class<?> clazz, String beanName, ConfigurableApplicationContext context) {
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        beanFactory.registerBeanDefinition(beanName, definition.getBeanDefinition());
        return true;
    }

    public static void unRegistrySpringBean(String beanName, ConfigurableApplicationContext context) {
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();
        if (beanFactory.containsBeanDefinition(beanName)) {
            beanFactory.removeBeanDefinition(beanName);
        }
    }

    public static Class<?> compilerClass(String code, String name) throws Exception {
        File outputDir = new File(System.getProperty("java.io.tmpdir"), "classes");
        if (!outputDir.exists()) {
            if (!outputDir.mkdirs()) {
                throw new RuntimeException("编辑扩展程序时，创建临时目录失败");
            }
        }
        return compilerClass(code, outputDir, name);
    }

    public static Class<?> compilerClass(String code, File outputDir, String name) throws Exception {
        JavaSourceFromString javaSource = new JavaSourceFromString(name, code);
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        if (compiler == null) {
            throw new IllegalStateException("无法获取Java编译器。请确保你正在使用JDK而不是JRE。");
        }
        // 创建标准的文件管理器
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        fileManager.setLocation(StandardLocation.CLASS_OUTPUT, List.of(outputDir));
        // 编译源代码
        Iterable<? extends JavaFileObject> compilationUnits = List.of(javaSource);
        JavaCompiler.CompilationTask task = compiler.getTask(
                null, // Writer for output messages (can be null)
                fileManager,
                null, // Diagnostic listener
                Arrays.asList("-classpath", System.getProperty("java.class.path")), // Compiler options
                null, // Classes to process (null means none)
                compilationUnits // Source files to compile
        );

        boolean success = task.call();
        // 关闭文件管理器
        fileManager.close();
        if (success) {
            return loadClass(name, outputDir);
        }
        return null;
    }

    private static Class<?> loadClass(String className, File outputDir) throws Exception {
        URL[] urls = {outputDir.toURI().toURL()};
        CustomClassLoader classLoader = new CustomClassLoader(urls, Thread.currentThread().getContextClassLoader(), outputDir);
        return classLoader.findClass(className);
    }

    public static void setFieldValue(Object object, Field field, Object value) {
        field.setAccessible(true);
        try {
            field.set(object, value);
        } catch (IllegalAccessException e) {
            log.error("无法设置字段值，field = {},object = {}, value = {}", field, object, value, e);
            throw new RuntimeException("设置数据失败");
        }
    }

    public static Object getFieldValue(Object obj, Field field) {
        Object value;
        if (null == obj || null == field) {
            return null;
        }
        try {
            if (field.canAccess(obj)) {
                // 可访问属性
                value = field.get(obj);
            } else {
                // 不可访问属性
                field.setAccessible(true);
                value = field.get(obj);
                field.setAccessible(false);
            }
        } catch (Exception e) {
            return null;
        }
        return value;
    }

    public static Object getFieldValue(Object obj, String field) {
        try {
            return getFieldValue(obj, obj.getClass().getDeclaredField(field));
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 解析 key:vlue形式参数age:20
     * 返回数组["age","20"]
     * 如果值为空返回["age",""]
     * 如果没有分隔符返回["age","age"]
     *
     * @param src src
     * @return String
     */
    public static String[] parseKeyValue(String src) {
        if (BasicUtil.isEmpty(src)) {
            return null;
        }
        int len = 2;
        String[] result;
        String key1 = src;
        String key2 = src;
        if (src.contains(":")) {
            String[] tmp = src.split(":");
            len = NumberUtil.max(len, tmp.length);
            result = new String[len];
            key1 = tmp[0];
            if (tmp.length > 1) {
                key2 = tmp[1];
            } else {
                key2 = "";
            }
            if (len - 2 >= 0) System.arraycopy(tmp, 2, result, 2, len - 2);
        } else {
            result = new String[2];
        }
        result[0] = key1;
        result[1] = key2;
        return result;
    }

    public static <T> List<T> array2list(T[]... arrays) {
        List<T> list = new ArrayList<>();
        if (null != arrays) {
            for (T[] array : arrays) {
                if (null != array) {
                    //list.addAll(Arrays.asList(array));
                    for (T item : array) {
                        if (item instanceof Collection) {
                            list.addAll((Collection) item);
                        } else {
                            list.add(item);
                        }
                    }
                }
            }
        }
        return list;
    }

    public static Map<String, Object> copy
            (Map<String, Object> into, Map<String, Object> copy, List<String> keys) {
        if (null == copy) {
            return into;
        }
        if (null != keys) {
            for (String key : keys) {
                String[] ks = parseKeyValue(key);
                assert ks != null;
                into.put(ks[0], copy.get(ks[1]));
            }
        }
        return into;
    }

    /**
     * 复制copy的属性值到to
     *
     * @param to   赋值给to
     * @param copy copy
     */
    public static void copyFieldValue(Object to, Object copy) {
        List<Field> fields = ClassUtil.getFields(to.getClass(), false, false);
        for (Field field : fields) {
            try {
                Field copyField = copy.getClass().getDeclaredField(field.getName());
                if (ClassUtil.hasField(copy.getClass(), field.getName()) &&
                        copyField.getType() == field.getType()) {
                    setFieldValue(to, field, getFieldValue(copy, field.getName()));
                }
            } catch (NoSuchFieldException ignored) {

            }

        }
    }

    /**
     * 集合拼接
     *
     * @param list     list
     * @param split    分隔符
     * @param required 是否必须(遇到宿舍是否忽略)
     * @return String
     */
    public static String concat(Collection<?> list, String split, boolean required) {
        StringBuilder builder = new StringBuilder();
        if (null != list) {
            for (Object item : list) {
                if (!required && BasicUtil.isEmpty(item)) {
                    continue;
                }
                if (!builder.isEmpty()) {
                    builder.append(split);
                }
                builder.append(item);
            }
        }
        return builder.toString();
    }

    public static String concat(Collection<?> list, String split) {
        return concat(list, split, false);
    }

    public static String concat(Collection<?> list, boolean required) {
        return concat(list, ",", required);
    }

    public static String concat(Collection<?> list) {
        return concat(list, false);
    }

    public static List<Integer> splitIntWithComma(String value) {
        if (StringUtils.isBlank(value)) {
            return Lists.newArrayList();
        }
        return Streams.of(value.split(",")).map(Integer::parseInt).collect(Collectors.toList());
    }

    static class JavaSourceFromString extends SimpleJavaFileObject {
        final String code;

        JavaSourceFromString(String name, String code) {
            super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
            this.code = code;
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return code;
        }
    }

    static class CustomClassLoader extends URLClassLoader {
        final File compileDir;

        public CustomClassLoader(URL[] urls, ClassLoader parent, File compileDir) {
            super(urls, parent);
            this.compileDir = compileDir;
        }

        @Override
        public Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] classData = loadClassData(name);
            return defineClass(name, classData, 0, classData.length);
        }

        private byte[] loadClassData(String className) throws ClassNotFoundException {
            try {
                File classFile = new File(compileDir, className.replace('.', '/') + ".class");
                byte[] buffer = new byte[(int) classFile.length()];
                try (java.io.FileInputStream fis = new java.io.FileInputStream(classFile)) {
                    fis.read(buffer);
                }
                return buffer;
            } catch (Exception e) {
                throw new ClassNotFoundException(className, e);
            }
        }
    }
}
